﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Management.Automation.Runspaces;
using System.Management.Automation;
using System.Collections.ObjectModel;
using System.IO;

namespace PowerShellFromNet_ScriptCommander
{
    public partial class FrmMain : Form
    {
        //TODO: Library path should be in config file
        string libraryPath = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "scriptlib");
        string scriptFilePath;

        public FrmMain()
        {
            InitializeComponent();
        }

        #region Events

        private void FrmMain_Load(object sender, EventArgs e)
        {
            // Load example script when starting the application
            string fileName = "RemoteServiceManager.ps1";
            scriptFilePath = GetLibraryScriptPath(fileName);
            LoadScript(scriptFilePath);
        }

        private void btnChooseScript_Click(object sender, EventArgs e)
        {
            // Open a file dialog, clear the parameter grid and load new script
            ofdScript.InitialDirectory = libraryPath;
            if (ofdScript.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                grdParameters.Rows.Clear();
                txtOutput.Clear();
                scriptFilePath = ofdScript.FileName;
                LoadScript(scriptFilePath);
            }
        }

        private void btnExecuteScript_Click(object sender, EventArgs e)
        {
            Dictionary<string, string> parameters = GetScriptParametersFromGrid(grdParameters);

            txtOutput.Text = RunPowerShellScript(scriptFilePath, parameters);
        }

        private void btnGetHelp_Click(object sender, EventArgs e)
        {
            txtOutput.Text = GetHelpPowerShellScript(scriptFilePath);
        }

        #endregion

        #region Basics

        /// <summary>
        /// Loads a PS script into the application.
        /// </summary>
        private void LoadScript(string scriptFilePath)
        {
            txtScriptName.Text = scriptFilePath;
            ExplorePowerShellScript(scriptFilePath);
        }

        /// <summary>
        /// Gets a path for a script using a fixed path. Useful when using a folder with a script library.
        /// </summary>
        private string GetLibraryScriptPath(string scriptName)
        {
            string scriptLocation = Path.Combine(libraryPath, scriptName);

            return @scriptLocation;
        }

        #endregion

        #region Parameters

        /// <summary>
        /// Gets the parameters from the PS script.
        /// </summary>
        private Dictionary<string, string> GetScriptParametersFromScript(string scriptPath)
        {
            if (!File.Exists(scriptPath)) { throw new Exception("no such file"); }

            Dictionary<string, string> parameters = new Dictionary<string, string>();
            string line;
            StreamReader file = new StreamReader(scriptPath);
            while ((line = file.ReadLine()) != null)
            {
                if (line.Contains("param("))
                {
                    //get parameternames
                    string[] linePieces = line.Split('$');
                    for (int i = 1; i < linePieces.Length; i++)
                    {
                        if (i != linePieces.Length - 1)
                        {
                            parameters.Add(linePieces[i].Split(',')[0].ToString(), null);
                        }
                        else
                        {
                            parameters.Add(linePieces[i].Split(')')[0].ToString(), null);
                        }
                        
                    }
                }
            }
            file.Close();

            return parameters;
        }

        /// <summary>
        /// Gets the filled in parameters from the grid.
        /// </summary>
        private Dictionary<string, string> GetScriptParametersFromGrid(DataGridView grid)
        {
            Dictionary<string, string> parameters = new Dictionary<string, string>();

            //get the parameters from the grid
            foreach (DataGridViewRow row in grid.Rows)
            {
                if (row.Cells["Name"].Value != null && row.Cells["Value"].Value != null)
                {
                    parameters.Add(row.Cells["Name"].Value.ToString(), row.Cells["Value"].Value.ToString());
                }
            }

            return parameters;
        }

        /// <summary>
        /// Discovers the parameters from the PS script and adds them to the grid.
        /// </summary>
        private void ExplorePowerShellScript(string scriptPath)
        {
            Dictionary<string, string> parameters = GetScriptParametersFromScript(scriptPath);
            foreach (KeyValuePair<string, string> scriptParameter in parameters)
            {
                grdParameters.ColumnCount = 2;
                grdParameters.Columns[0].Name = "Name";
                grdParameters.Columns[0].ReadOnly = true;
                grdParameters.Columns[1].Name = "Value";

                string[] row = { scriptParameter.Key, scriptParameter.Value };
                grdParameters.Rows.Add(row);
            }
        }

        #endregion

        #region Script
        
        /// <summary>
        /// Tries to run the PS script using filled in parameters.
        /// </summary>
        private string RunPowerShellScript(string scriptPath, Dictionary<string, string> parameters)
        {
            // Validate file and parameters
            if (string.IsNullOrEmpty(scriptPath) || !File.Exists(scriptPath)) { throw new ArgumentNullException("scriptFile"); }
            //if (parameters == null) { throw new ArgumentNullException("parameters"); }

            RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
            Collection<PSObject> psObjects;
            string error = "";
            StringBuilder sb = new StringBuilder();

            using (Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration))
            {
                runspace.Open();
                RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
                scriptInvoker.Invoke("Set-ExecutionPolicy Unrestricted");
                Pipeline pipeline = runspace.CreatePipeline();

                Command scriptCommand = new Command(scriptPath);
                if (parameters != null)
                {
                    Collection<CommandParameter> commandParameters = new Collection<CommandParameter>();
                    foreach (KeyValuePair<string, string> scriptParameter in parameters)
                    {
                        CommandParameter commandParam = new CommandParameter(scriptParameter.Key, scriptParameter.Value);
                        commandParameters.Add(commandParam);
                        scriptCommand.Parameters.Add(commandParam);
                    }
                }

                pipeline.Commands.Add(scriptCommand);
                pipeline.Commands.Add("Out-String");

                try
                {
                    psObjects = pipeline.Invoke();

                    foreach (PSObject obj in psObjects)
                    {
                        sb.AppendLine(obj.ToString());
                    }
                }
                catch (Exception ex)
                {
                    error = ex.Message;
                }
            }

            if (String.IsNullOrEmpty(error))
            {
                return sb.ToString();
            }
            else
            {
                return error;
            }
        }

        /// <summary>
        /// Tries to get the help from the PS script.
        /// </summary>
        private string GetHelpPowerShellScript(string scriptPath)
        {
            // Validate file
            if (string.IsNullOrEmpty(scriptPath) || !File.Exists(scriptPath)) { throw new ArgumentNullException("scriptFile"); }

            RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
            Collection<PSObject> psObjects;
            string error = "";
            StringBuilder sb = new StringBuilder();

            using (Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration))
            {
                runspace.Open();
                RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
                scriptInvoker.Invoke("Set-ExecutionPolicy Unrestricted");
                Pipeline pipeline = runspace.CreatePipeline();

                // Add quotes to the pathstring first, in case there are spaces in it
                scriptPath = String.Concat("\"", scriptPath, "\"");
                pipeline.Commands.AddScript(String.Concat("Get-Help ", @scriptPath));
                pipeline.Commands.Add("Out-String");

                try
                {
                    psObjects = pipeline.Invoke();

                    foreach (PSObject obj in psObjects)
                    {
                        sb.AppendLine(obj.ToString());
                    }
                }
                catch (Exception ex)
                {
                    error = ex.Message;
                }
            }

            if (String.IsNullOrEmpty(error))
            {
                return sb.ToString();
            }
            else
            {
                return error;
            }
        }

        #endregion
    }
}